
import os
import wx
import re

import ConfigUtil

##################################################

configdict = None

console_background = None

default_font = None
default_bfont = None

console_input_attr = None
console_output_attr = None
console_error_attr = None
console_end_attr = None

def InitConfig():
	global configdict
	if configdict:
		LoadLocalConfig()
		configdict = None

	LoadDefaultConfig()

def LoadDefaultConfig():
	global default_font, default_bfont
	global console_input_attr, console_output_attr, console_error_attr, console_end_attr
	default_font = default_font or wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False, 'Courier New')
	default_bfont = default_bfont or wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Courier New')

	console_input_attr = console_input_attr or wx.TextAttr("black", wx.NullColour, default_bfont)
	console_output_attr = console_output_attr or wx.TextAttr("black", wx.NullColour, default_font)
	console_error_attr = console_error_attr or wx.TextAttr("red", wx.NullColour, default_font)
	console_end_attr = console_end_attr or wx.TextAttr("orange", wx.NullColour, default_font)

def LoadLocalConfig():
	global configdict
	global console_background, default_font, default_bfont
	global console_input_attr, console_output_attr, console_error_attr, console_end_attr
	if configdict.has_key('console_default_font'):
		default_font = ConfigUtil.ParseFont(configdict['console_default_font'])
	if configdict.has_key('console_output_attr'):
		console_output_attr = ConfigUtil.ParseTextAttr(configdict['console_output_attr'])
	if configdict.has_key('console_input_attr'):
		console_input_attr = ConfigUtil.ParseTextAttr(configdict['console_input_attr'])
	if configdict.has_key('console_error_attr'):
		console_error_attr = ConfigUtil.ParseTextAttr(configdict['console_error_attr'])
	if configdict.has_key('console_end_attr'):
		console_end_attr = ConfigUtil.ParseTextAttr(configdict['console_end_attr'])
	if configdict.has_key('console_background'):
		console_background = configdict['console_background']

def SetConfigDict(d):
	global configdict
	configdict = d


##################################################

class ConsolePanel(wx.Panel):
	def __init__(self, parent, ID):
		wx.Panel.__init__(self, parent, ID)

		self.process = None
		self.Bind(wx.EVT_IDLE, self.OnIdle)

		# We can either derive from wx.Process and override OnTerminate
		# or we can let wx.Process send this window an event that is
		# caught in the normal way...
		self.Bind(wx.EVT_END_PROCESS, self.OnProcessEnded)

		self.output = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE|wx.TE_RICH2)
		self.output.SetEditable(False)

		# TODO: support other platform
		#self.font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False, 'Courier New')
		#self.bfont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Courier New')
		#self.inputAttr = wx.TextAttr("black", wx.NullColour, self.bfont)
		#self.outputAttr = wx.TextAttr("black", wx.NullColour, self.font)
		#self.errorAttr = wx.TextAttr("red", wx.NullColour, self.font)
		#self.endAttr = wx.TextAttr("orange", wx.NullColour, self.font)
		#self.output.SetFont(self.font)

		InitConfig()
		global console_background, default_font
		global console_input_attr, console_output_attr, console_error_attr, console_end_attr
		self.inputAttr = console_input_attr
		self.outputAttr = console_output_attr
		self.errorAttr = console_error_attr
		self.endAttr = console_end_attr
		self.output.SetFont(default_font)
		if console_background:
			self.output.SetBackgroundColour(console_background)


		self.output.Bind(wx.EVT_KEY_DOWN, self.OnChar)


		box1 = wx.BoxSizer(wx.HORIZONTAL)

		sizer = wx.BoxSizer(wx.VERTICAL)
		sizer.Add(self.output, 1, wx.EXPAND|wx.ALL, 2)
		sizer.Add(box1, 0, wx.EXPAND|wx.ALL, 2)

		self.SetSizer(sizer)
		self.SetAutoLayout(True)


		self.OnTerminateHandler = None


	def __del__(self):
		if self.process is not None:
			self.process.Detach()
			self.process.CloseOutput()
			self.process = None


	def Run(self, cmd):
		self.output.Clear()

		self.process = wx.Process(self)
		self.process.Redirect();
		self.pid = wx.Execute(cmd, wx.EXEC_ASYNC, self.process)

		self.output.SetEditable(True)

	def Run2(self, cmd):
		self.process = wx.Process(self)
		self.process.Redirect();
		wx.Execute(cmd, wx.EXEC_ASYNC, self.process)
		self.output.SetEditable(True)

	def Kill(self):
		if self.process == None:
			return
		assert self.pid and wx.Process.Exists(self.pid), 'PID: '+self.pid+" doesn't exist"
		#wx.Process.Kill(self.pid, wx.SIGTERM, wx.KILL_CHILDREN)
		wx.Process.Kill(self.pid, wx.SIGKILL, wx.KILL_CHILDREN)

	def CloseStream(self):
		if self.process == None:
			return
		self.process.CloseOutput()
		self.output.SetEditable(False)

	def OnChar(self, event):
		if not self.output.IsEditable():
			event.Skip()
			return

		key = event.GetKeyCode()
		ctrl = event.CmdDown()
		if key == wx.WXK_ESCAPE:
			self.output.Remove(self.output.last, self.output.GetLastPosition())
			return
		elif key == wx.WXK_RETURN:
			text = self.output.GetRange(self.output.last, self.output.GetLastPosition())
			text = text + '\n'
			self.AppendInput('\n')
			self.SendText(text)
			return
		elif key == wx.WXK_BACK:
			if self.output.GetInsertionPoint() <= self.output.last:
				return
		elif key == wx.WXK_DELETE:
			if self.output.GetInsertionPoint() < self.output.last:
				return
		elif ctrl and (key == ord('Z') or key == ord('D')):
			self.CloseStream()
			return
		elif ctrl and key == ord('C'):
			self.Kill()
			return

		event.Skip()

	def OnIdle(self, evt):
		if self.process is not None:
			stream = self.process.GetErrorStream()
			if stream.CanRead():
				text = self.Read(stream)
				self.AppendError(text)

			stream = self.process.GetInputStream()
			if stream.CanRead():
				text = self.Read(stream)
				self.AppendOutput(text)


	def OnProcessEnded(self, evt):
		stream = self.process.GetErrorStream()
		if stream.CanRead():
			text = self.Read(stream)
			self.AppendError(text)

		stream = self.process.GetInputStream()
		if stream.CanRead():
			text = self.Read(stream)
			self.AppendOutput(text)

		self.process.Destroy()
		self.process = None
		self.pid = None

		self.AppendText('\n', self.endAttr)
		self.AppendText('==================================================\n', self.endAttr)
		self.AppendText('====================    END   ====================\n', self.endAttr)
		self.AppendText('==================================================\n', self.endAttr)
		self.output.SetEditable(False)

		if self.OnTerminateHandler:
			self.OnTerminateHandler()


	def SendText(self, text):
		self.Write(self.process.GetOutputStream(), text)

	def Read(self, stream):
		text = stream.read()
		if wx.USE_UNICODE:
			import sys
			text = text.decode(sys.getfilesystemencoding())
		return text

	def Write(self, stream, text):
		if wx.USE_UNICODE:
			import sys
			text = text.encode(sys.getfilesystemencoding())
		stream.write(text)
	
	def AppendOld(self, text):
		self.output.AppendText(text)
		self.output.Update()
	
	def AppendInput(self, text):
		self.AppendText(text, self.inputAttr)

	def AppendOutput(self, text):
		self.AppendText(text, self.outputAttr)

	def AppendError(self, text):
		self.AppendText(text, self.errorAttr)
	
	def AppendText(self, text, textAttr):
		p1 = self.output.GetLastPosition()
		n = len(text)
		p2 = p1 + n

		self.Append(text)
		self.output.SetStyle(p1, p2, textAttr)
		self.output.SetStyle(p2, p2, self.inputAttr)

	
	def Append(self, text):
		self.output.AppendText(text)
		#self.output.WriteText(text)
		self.output.ScrollLines(self.output.GetNumberOfLines())
		self.output.SetFocus()
		self.output.last = self.output.GetLastPosition()
		#self.output.Update()

	##################################################
	def RegisterTerminate(self, handler):
		self.OnTerminateHandler = handler

##################################################


def runTest(frame, nb, log):
	p = ConsolePanel(frame, -1)
	p.Run('lua -i')

	return p


if __name__ == '__main__':
	import sys,os
	import wxrun
	wxrun.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])

